/**
 * 
 */
package neptune.manager.storage;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import neptune.manager.model.template.VmTemplate;
import neptune.manager.model.topology.OperativeSystem;
import neptune.manager.model.topology.Software;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
 * @author Roberto Bifulco [RobertoBifulco.it] [info@robertoBifulco.it]
 * 
 */
public class XMLVmTemplateRepository implements VmTemplateRepository {

	// TODO aggiungere controlli sulla correttezza del formato del file xml
	// letto
	private String filepath;

	public XMLVmTemplateRepository(String filepath) {
		if (!filepath.endsWith(File.separator)) {
			filepath += File.separator;
		}
		this.filepath = filepath;
	}

	public VmTemplate readXMLVmTemplate(String vmTemplateId)
			throws VmTemplateRepositoryException {
		VmTemplate vmTemplate = new VmTemplate();

		File xmlFile = new File(filepath + vmTemplateId + ".xml");

		DocumentBuilderFactory docFactory = DocumentBuilderFactory
				.newInstance();
		docFactory.setValidating(false);

		try {
			Document doc = docFactory.newDocumentBuilder().parse(xmlFile);

			Element tmpEl = doc.getDocumentElement();
			String tmpId = tmpEl.getAttribute("id");

			vmTemplate.setId(tmpId);

			NodeList itemChilds = tmpEl.getChildNodes();
			for (int i = 0; i < itemChilds.getLength(); i++) {
				if (!itemChilds.item(i).getNodeName().equals("#text")) {

					String nodeName = itemChilds.item(i).getNodeName();

					if (nodeName.equals("operative_system")) {

						Element osEl = (Element) itemChilds.item(i);
						OperativeSystem os = new OperativeSystem(osEl
								.getAttribute("name"), osEl
								.getAttribute("version"), osEl
								.getAttribute("username"), osEl
								.getAttribute("password"));
						vmTemplate.setOperativeSystem(os);

					} else if (nodeName.equals("software_configuration")) {

						Element softwareConfigurationEl = (Element) itemChilds
								.item(i);

						Element sshServer = (Element) softwareConfigurationEl
								.getElementsByTagName("sshServer").item(0);
						vmTemplate.setControlPort(Integer.parseInt(sshServer
								.getAttribute("port")));

						NodeList softwares = softwareConfigurationEl
								.getElementsByTagName("software");
						ArrayList<Software> sws = new ArrayList<Software>(0);
						for (int j = 0; j < softwares.getLength(); j++) {
							Element softwareEl = (Element) softwares.item(j);
							sws.add(new Software(softwareEl
									.getAttribute("name"), softwareEl
									.getAttribute("version"), softwareEl
									.getAttribute("type")));
						}
						vmTemplate.setSoftwares(sws);

					}
				}
			}

			return vmTemplate;
		} catch (Exception e) {
			throw new VmTemplateRepositoryException(e);
		}
	}

	private String getXML(VmTemplate template)
			throws VmTemplateRepositoryException {
		DocumentBuilderFactory docFactory = DocumentBuilderFactory
				.newInstance();
		docFactory.setValidating(false);

		try {
			Document document = docFactory.newDocumentBuilder().newDocument();
			document.setXmlVersion("1.0");

			Element root = document.createElement("vmTemplate");
			root.setAttribute("id", template.getId());
			document.appendChild(root);

			if (template.getOperativeSystem() != null) {
				Element os = document.createElement("operative_system");
				os
						.setAttribute("name", template.getOperativeSystem()
								.getName());
				os.setAttribute("version", template.getOperativeSystem()
						.getVersion());
				os.setAttribute("username", template.getOperativeSystem()
						.getAdminUsername());
				os.setAttribute("password", template.getOperativeSystem()
						.getAdminPassword());
				root.appendChild(os);
			}

			Element softConf = document.createElement("software_configuration");
			softConf.setAttribute("template", template.getId());
			root.appendChild(softConf);

			Element sshElement = document.createElement("sshServer");
			sshElement.setAttribute("port", Integer.toString(template
					.getControlPort()));
			softConf.appendChild(sshElement);

			for (Software soft : template.getSoftwares()) {
				Element softEl = document.createElement("software");
				softEl.setAttribute("name", soft.getId());
				softEl.setAttribute("type", soft.getType());
				softEl.setAttribute("version", soft.getVersion());
				softConf.appendChild(softEl);
			}

			// ///////////////
			// Output the XML

			// set up a transformer
			TransformerFactory transfac = TransformerFactory.newInstance();
			Transformer trans = transfac.newTransformer();
			trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
			trans.setOutputProperty(OutputKeys.INDENT, "yes");
			trans.setOutputProperty(OutputKeys.STANDALONE, "yes");
			trans.setOutputProperty(
					"{http://xml.apache.org/xslt}indent-amount", "2");

			// create string from xml tree
			StringWriter sw = new StringWriter();
			StreamResult result = new StreamResult(sw);
			DOMSource source = new DOMSource(document);
			trans.transform(source, result);
			return sw.toString();

		} catch (ParserConfigurationException e) {
			throw new VmTemplateRepositoryException(e);
		} catch (TransformerException e) {
			throw new VmTemplateRepositoryException(e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * neptune.manager.storage.VmTemplateRepository#getVmTemplateByName(java
	 * .lang.String)
	 */
	@Override
	public VmTemplate getVmTemplateByName(String vmTemplateId)
			throws VmTemplateRepositoryException {
		return readXMLVmTemplate(vmTemplateId);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see neptune.manager.storage.VmTemplateRepository#getVmTemplateNames()
	 */
	@Override
	public String[] getVmTemplateNames() {
		File vmTmpDir = new File(filepath);

		if (!vmTmpDir.exists())
			return new String[] {};

		String[] list = vmTmpDir.list(new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				return name.matches(".*\\.xml"); // considera solo i file .xml
			}
		});

		for (int i = 0; i < list.length; i++) {
			list[i] = list[i].split("\\.")[0];
		}

		return list;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * neptune.manager.storage.VmTemplateRepository#addVmTemplate(java.lang.
	 * String, java.io.InputStream)
	 */
	@Override
	public void addVmTemplate(VmTemplate template, InputStream templateData)
			throws VmTemplateRepositoryException {

		try {
			File dest = new File(filepath + template.getId() + ".img");
			if (dest.exists())
				throw new VmTemplateRepositoryException("Template '"
						+ template.getId() + "' already exists!");
			dest.createNewFile();

			FileOutputStream fos = new FileOutputStream(dest);

			byte[] buf = new byte[4096];
			int i = 0;
			while ((i = templateData.read(buf)) != -1)
				fos.write(buf, 0, i);

			File xmlFile = new File(filepath + template.getId() + ".xml");
			String xmlString = getXML(template);
			xmlFile.createNewFile();
			FileWriter writer = new FileWriter(xmlFile);
			writer.write(xmlString);
			writer.flush();

		} catch (FileNotFoundException e) {
			throw new VmTemplateRepositoryException(e);
		} catch (IOException e) {
			throw new VmTemplateRepositoryException(e);
		}

	}
}
